package top.guinguo.poker;

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 游戏类
 */
public class Game {

    /**
     * 玩家
     */
    private List<Player> players = new ArrayList<Player>(6);

    /**
     * 扑克牌
     * 使用队列，不用自己去除扑克牌
     */
    private Queue<Poker> pokerQueue;

    /**
     * 玩家数量
     */
    private int playNum;

    /**
     * 庄家的号码
     * 庄家是随机选的
     */
    private int masterNum;

    /**
     * 默认容忍度，容忍度越低，点数要求越高
     * 1.0 表示当点数=[8,9,10,10.5]时不再要牌
     * 0.8-1.0 表示当点数=[8.5,9,10,10.5]时不再要牌
     * 0.5-0.8 表示当点数=[9.5,10,10.5]时不再要牌
     * 0.1-0.5 表示当点数=[10,10.5]时不再要牌
     * 0.1 表示当点数=[10.5]时不再要牌
     */
    private double tolerance = 1.0;

    /**
     * 用于生产随机数
     */
    private Random random = new Random();

    public static void main(String[] args) {
        Game game = new Game();
        game.start();
    }

    private void start() {
        /**
         * 游戏分4部分，准备，下注，开始，结束
         */
        init();//准备，初始化扑克牌，确定玩家，容忍度
        bet();//自动下注
        play();//开始游戏
        end();//结束，打印结果
    }

    /**
     * 初始化
     */
    private void init() {
        List<Poker> pokerList = new ArrayList<Poker>(Arrays.asList(
                new Poker("A", Poker.Color.club, 1.0),
                new Poker("A", Poker.Color.diamand, 1.0),
                new Poker("A", Poker.Color.heart, 1.0),
                new Poker("A", Poker.Color.spade, 1.0),

                new Poker("J", Poker.Color.spade, 0.5),
                new Poker("J", Poker.Color.heart, 0.5),
                new Poker("J", Poker.Color.diamand, 0.5),
                new Poker("J", Poker.Color.club, 0.5),

                new Poker("Q", Poker.Color.club, 0.5),
                new Poker("Q", Poker.Color.heart, 0.5),
                new Poker("Q", Poker.Color.diamand, 0.5),
                new Poker("Q", Poker.Color.spade, 0.5),

                new Poker("K", Poker.Color.spade, 0.5),
                new Poker("K", Poker.Color.club, 0.5),
                new Poker("K", Poker.Color.diamand, 0.5),
                new Poker("K", Poker.Color.heart, 0.5)
        )
        );

        for (int i = 2; i <= 10; i++) {
            for (Poker.Color color : Poker.Color.values()) {
                Poker poker = new Poker(i + "", color, i);
                pokerList.add(poker);
            }
        }

        //洗牌
        Collections.shuffle(pokerList);

        /**
         * 采用Linked是因为底层删除比线性表快，而使用阻塞队列是考虑到可能有多个人同时要牌，就要等待
         */
        pokerQueue = new LinkedBlockingQueue<Poker>(pokerList);

        //输入玩家数量
        playNum = DialogUtil.InputNumber("请输入玩家数量，2-6之间");

        //输入游戏容忍度
        tolerance = DialogUtil.InputDouble("请输游戏容忍度，0-1.0之间\n"+
                        "默认容忍度，容忍度越低，点数要求越高\n"+
                        "1.0 表示当点数=[8,9,10,10.5]时不再要牌\n"+
                        "0.8-1.0 表示当点数=[8.5,9,10,10.5]时不再要牌\n"+
                        "0.5-0.8 表示当点数=[9.5,10,10.5]时不再要牌\n"+
                        "0.1-0.5 表示当点数=[10,10.5]时不再要牌\n"+
                        "0.1 表示当点数=[10.5]时不再要牌\n"
        );
        while (tolerance < 0.1 || tolerance > 1.0) {
            tolerance = DialogUtil.InputDouble("请输游戏容忍度，0-1.0之间\n"+
                            "默认容忍度，容忍度越低，点数要求越高\n"+
                            "1.0 表示当点数=[8,9,10,10.5]时不再要牌\n"+
                            "0.8-1.0 表示当点数=[8.5,9,10,10.5]时不再要牌\n"+
                            "0.5-0.8 表示当点数=[9.5,10,10.5]时不再要牌\n"+
                            "0.1-0.5 表示当点数=[10,10.5]时不再要牌\n"+
                            "0.1 表示当点数=[10.5]时不再要牌\n"
            );
        }

        //庄家号码
        masterNum = random.nextInt(playNum);

        /**
         * 初始化玩家
         */
        String wj = "";
        for (int i = 0; i < playNum; i++) {
            Player player = new Player("玩家" + (i + 1), false);
            if (i == masterNum) {
                player.setIsMaster(true);
            } else {
                wj+=",玩家"+ (i + 1);
            }
            players.add(player);
        }

        DialogUtil.PrintText("玩家" + (masterNum + 1) + "被选为庄家"+wj+"为闲家");
        DialogUtil.PrintText("--------------------------------------------------");
    }

    /**
     * 自动下注
     */
    private void bet() {
        DialogUtil.PrintText("-----------下注----------");
        for (Player player : players) {
            if (!player.isMaster()) {
                int betMoney = random.nextInt(1000) + 1;
                player.setBet(betMoney);
                DialogUtil.PrintText(player.getName() + "下注：" + betMoney + " 元");
            }
        }
        DialogUtil.PrintText("-----------下注----------");
    }

    /**
     * 开始游戏
     */
    private void play() {
        DialogUtil.PrintText("===========发牌===========");
        //要牌顺序
        String order = "要牌顺序：";
        //玩家名称
        String firstPoker = "发牌，"+(playNum) + "位玩家分别得到牌";
        //每个人发一张牌
        for (Player player : players) {
            Poker poker = sendPoker(player);
            DialogUtil.PrintText(player.getName() + "要到一张" + poker);
            firstPoker += "," + player.getName() + "->" + poker.getPoint();
            if (!player.isMaster()) {
                order += player.getName()+"，";
            }
        }
        order += players.get(masterNum).getName();
        DialogUtil.PrintText(firstPoker);
        DialogUtil.PrintText(order);
        DialogUtil.PrintText("--------------------------------------------------");

        /**
         * 发牌
         */
        for (Player player : players) {
            if (!player.isMaster()) {
                turnTo(player);
            }
        }

        //给庄家发牌
        turnTo(players.get(masterNum));

        DialogUtil.PrintText("--------------------------------------------------");
    }


    /**
     * 向玩家发牌
     * @param player
     * @return
     */
    private Poker sendPoker(Player player) {
        //add poker
        Poker poker = pokerQueue.poll();
        player.addPoker(poker);
        return poker;
    }

    /**
     * 轮转
     * @param player
     */
    private void turnTo(Player player) {
        boolean need = check(player);
        if (!need) {
            DialogUtil.PrintText(player.getName() + "不要牌，总点数：" + player.getPokerPoint());
            return;
        }
        String msg = player.getName()+":";
        int count=0;
        while (need) {
            Poker poker = sendPoker(player);
            msg += getStatus(player, poker, count++);
            need = check(player);
        }
        DialogUtil.PrintText(msg);
    }

    /**
     * 1.0 表示当点数=[8,9,10,10.5]时不再要牌
     * 0.8-1.0 表示当点数=[8.5,9,10,10.5]时不再要牌
     * 0.5-0.8 表示当点数=[9.5,10,10.5]时不再要牌
     * 0.1-0.5 表示当点数=[10,10.5]时不再要牌
     * 0.1 表示当点数=[10.5]时不再要牌
     */
    private boolean check(Player player) {
        double low;
        if (tolerance == 1.0) {
            low = 8.0;
        } else if (tolerance >= 0.8) {
            low = 8.5;
        } else if (tolerance >= 0.5) {
            low = 9.5;
        } else if (tolerance > 0.1) {
            low = 10;
        } else low = 10.5;

        if (player.getPokerCount() == 5 && player.getPokerPoint() <= 10.5) {
            return false;
        } else if (player.getPokerPoint() >= low) {
            return false;
        }

        return true;
    }

    private String getStatus(Player player, Poker poker, int count) {
        String msg = "";
        if (count == 0) {
            msg = "要牌，得到" + poker.getPoint()+"，";
        } else if (count == 1) {
            msg = "再要牌得到" + poker.getPoint()+"，";
        } else if (count == 2) {
            msg = "再要牌得到" + poker.getPoint()+"，";
        } else if (count == 3) {
            msg = "再要牌得到" + poker.getPoint()+"，";
        } else if (count == 4) {
            msg = "再要牌得到" + poker.getPoint()+"，";
        }

        if (!check(player)) {
            if (player.getPokerPoint() <= 10.5) {
                msg += "停牌，总点数：" + player.getPokerPoint();
            } else {
                msg += "最后总点数为" + player.getPokerPoint() + "，爆煲";
            }
        }
        return msg;
    }

    /**
     * 结束游戏，打印输出结果
     */
    private void end() {
        String msg = "";
        Player master = players.get(masterNum);
        if (masterWinAll(master) > 0) {
            if (masterWinAll(master) == 1) {
                msg = master.getName() +
                        "“五龙”" + master.getPokerList() + "，所有玩家立赔";
            } else {
                msg = master.getName() +
                        "“十点半”" + master.getPokerList() + "，所有玩家立赔";
            }
            DialogUtil.PrintText(msg);
        } else {
            for (Player player : players) {
                if (!player.isMaster()) {
                    if (player.getPokerCount() == 5 && player.getPokerPoint() <= 10.5) {
                        msg = player.getName() +
                                "“五龙”" + player.getPokerList() + "，庄家"+master.getPokerList()+"立赔" + player.getBet() + "元";
                    } else if (player.getPokerCount() == 2 && player.getPokerPoint() == 10.5) {
                        msg = player.getName() +
                                "“十点半”" + player.getPokerList() + "，庄家"+master.getPokerList()+"立赔" + player.getBet() + "元";
                    } else if (player.getPokerPoint() > 10.5) {
                        msg = player.getName() +
                                "“爆煲(Busted)”" + player.getPokerList() + "，玩家立赔" + player.getBet() + "元";
                    } else {
                        if (master.getPokerPoint() <= 10.5) {
                            if (player.getPokerPoint() > master.getPokerPoint()) {
                                msg = player.getName() +
                                        "“未爆煲”" + player.getPokerList() + "，庄家"+master.getPokerList()+"立赔" + player.getBet() + "元";
                            } else if (player.getPokerPoint() == master.getPokerPoint()) {
                                msg = player.getName() +
                                        "“食夹棍”" + player.getPokerList() + "，玩家立赔" + player.getBet() + "元";
                            }
                        } else {
                            msg = player.getName() + player.getPokerList() + "，“庄家爆煲”庄家立赔" + player.getBet() + "元";
                        }
                    }
                    DialogUtil.PrintText(msg);
                }
            }
        }
        DialogUtil.PrintText("---------------------游戏结束----------------------");

    }

    /**
     * 判断庄家是否通吃
     * @param master
     * @return
     */
    private int masterWinAll(Player master) {
        if (master.getPokerCount() == 5 && master.getPokerPoint() <= 10.5) {
            return 1;
        } else if (master.getPokerCount() == 2 && master.getPokerPoint() == 10.5) {
            return 2;
        }
        return 0;
    }
}
